package org.fjsei.yewu.bpm;

//import io.camunda.operate.CamundaOperateClient;
import io.camunda.operate.CamundaOperateClient;
import io.camunda.operate.exception.OperateException;
import io.camunda.operate.model.*;
//import io.camunda.client.api.search.response.Incident;
import io.camunda.operate.search.*;
import io.camunda.tasklist.dto.DateFilter;
import md.cm.geography.AdminunitRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
import java.util.Date;
import java.util.List;

import static graphql.Assert.assertShouldNeverHappen;

/**文档对应于 https://github.com/camunda-community-hub/camunda-operate-client-java

 * starter自带  https://github.com/camunda-community-hub/spring-zeebe/tree/main/camunda-sdk-java/java-client-operate
 * [文档]Plain Java模式足够：  https://github.com/camunda-community-hub/camunda-operate-client-java
 * 实际上又套了一层：我这通过camunda-operate-client-java包来对接Operate服务器提供的API接口，最后Operate的存储实际挖掘来自ES索引存储zeebe_record，
 * 而zeebe_record索引是由流程引擎服zeebe主动导出的, zeebe/Broker.bat它实际内部也有自己管理的最原始存储的。倒腾2遍的流程数据啊。
   Zeebe内部流程数据 => 主动导出到ES索引zeebe-record*存储 => 再次由camunda-operate服务端启动后会收集整理为ES索引operate-*存储。
 *【目的】实际Operate服务器启动的用处是为了查询Incident列表的，方便解决内部隐藏的流程问题。#@而且Tasklist服务器和Operate真没有依赖关联的。
 * [有问题]有这可能性，zeebe流程引擎集群自身的流程存储的有些流程实例会遗留下，客户端也一直无法知晓这些流程实例的存在的，无法自动清除被遗忘的引擎存储数据。
 * Elastic exporter如何可以把zeebe流程引擎的内部存在的历史流程实例数据全量导出？ 【思路】在关系数据库保留流程创建的历史记录，依赖外部数据库确保zeebe历史流程的清理完整性。
 tasklist-索引，operate-索引 以及*_Expoter输出zeebe-record_索引, 这三类后面名字加了日期的都是旧的数据索引，可以删除；zeebe-record_当日的不能随便删。
 zeebe这三个大块的ES索引都类似处置的，索引没用的尾部加日期。在zeebe-record_索引数据没有缺失情景下，若直接删除Operate Tasklist两组索引后重启，有可能自动恢复旧的流程实例。
 手动清理Operate Tasklist旧数据操作根本不会影响到zeebe-record_索引，又会自动会恢复啊，所以zeebe-record_肯定首先清理掉的，然后才是清理后面2个=Operate Tasklist两组索引。
 这里API接口实际对接的是Operate-这个Web服务器的，Operate是二传手一样再发送给zeebe核心引擎去: camunda-operate-client-java。
 其实：spring-zeebe{改名io.camunda.spring:spring-boot-starter-camunda:8.2}这个包又是和tasklist-client-java包没有直接关系的。
 原先是https://github.com/camunda-community-hub/spring-zeebe搞的'io.camunda':'spring-zeebe-starter'，如今改成'io.camunda.spring:spring-boot-starter-camunda:'。
 从8.2版本来看https://github.com/camunda-community-hub/camunda-operate-client-java是必备的关键客户端API。 反而这个tasklist-服务包是补充非一定要安装。
 以后有可能tasklist完全没必要搭建的？，目前还需要UserTask列表功能，还没法让operate-client来做替代的。
 【Message有关】只能用zeebe-client-java-最底层包来做，或者zbctl命令行来做:消息触发？还不如直接应用层关联定位方便了：避免流程实例长期滞留引擎中。消息驱动流程图？感觉像鸡肋！newPublishMessageCommand();
 BPMN做法：有些事情并不一定都要依赖流程引擎来定义来执行来写的代码或者状态变量赋能的，实际完全可以仅仅是一个附加功能，反而正常后端应用服务器的代码或模型变量字段以及后端代码应该当作主体才对,避免本末倒置。
 上层的包spring-zeebe-*部分：没找到newPublishMessageCommand有关部分; 我这基本依赖上层包，原始底层zeebe-client-包没有直接使用！
 * */
@Service
public class IncidentProcessListService {
//    SimpleAuthentication sa;
    CamundaOperateClient client;
    //开关， 启用 流程引擎 相关的几个模块吗？
    Boolean useZeebe;
    //简单独立部署环境用的：To use if Tasklist is not configured with Identity and Keycloak情况用的。
    public IncidentProcessListService(AdminunitRepository adminunitRepository, @Value("${zeebe.client.enabled:true}")  Boolean useZeebe) {
        this.useZeebe=useZeebe;
        if(useZeebe) {
//            sa = new SimpleAuthentication("herzhang", "testuko", "http://localhost:8271");
        }
    }
    @PostConstruct       //启动时间Operate服务器重建索引啊，耗时间
    public void initialize() throws OperateException {
        if(useZeebe){
//                client = new CamundaOperateClient.Builder().operateUrl("http://localhost:8271").authentication(sa).build();
        }
    }
    /**只保留故障列表的功能：Operate服务器所能提供的其它功能能直接用zeebe_client接口替代。
     * */
    public List<Incident> getIncidents(Integer pageSize) {
        if(null!=client) {
            try {
                //search incidents based on filters
//                IncidentFilter incidentFilter =IncidentFilter.builder().creationTime(new DateFilter(new Date(), DateFilterRange.YEAR)).build();
//                SearchQuery incidentQuery = new SearchQuery.Builder().filter(incidentFilter).size(20).sort(new Sort("state", SortOrder.ASC)).build();
                IncidentFilter incidentFilter = IncidentFilter.builder()
                        .creationTime(new OperateDate.OperateDateFilter(new Date(), DateFilterRange.YEAR)).build();
                SearchQuery incidentQuery = new SearchQuery.Builder().withFilter(incidentFilter).withSize(pageSize).withSort(new Sort("state", SortOrder.ASC)).build();
                List<Incident> incidents = client.searchIncidents(incidentQuery);    //若连接服务端是camunda-tasklist的这里才报错的;要配对服务端。
                int size = incidents.size();
                if(size>0) {
                    Incident incident = client.getIncident(incidents.getFirst().getKey());
                }
                return incidents;
            } catch (OperateException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**判定特定流程实例的状态能否安全删除： Operate服务器的ES-索引存储反而是关键数据。zeebe-Broker最核心的存储文件数据实际反而变成流数据或者状态消息切换的凭据。
     * 应用层实际直接依据Operate API获得的流程状态。
     * ? Operate服务器所能提供的其它功能能直接用zeebe_client接口替代。
     * 【问题】这里设置单步端点可能影响到了其它人直接在camunda-Operate网站请求的应答？竟然能锁住？？
     * 参数 prId也不是随意的？随意long可能报错 /v1/process-instances/34234 : 401 不是404错误。
     * */
    public boolean canDeleteProcessInstance(Long prId) {
        if(null!=client) {
            try {
//                ProcessInstanceFilter instanceFilter = new ProcessInstanceFilter.Builder().bpmnProcessId("customer_onboarding_en").startDate(new DateFilter(new Date(), DateFilterRange.MONTH)).build();
//                SearchQuery instanceQuery = new SearchQuery.Builder().filter(instanceFilter).size(20).sort(new Sort("state", SortOrder.ASC)).build();
//                List<ProcessInstance> list = client.searchProcessInstances(instanceQuery);
//                SearchResult<ProcessInstance> result = client.searchProcessInstanceResults(instanceQuery);
                ProcessInstance instance = client.getProcessInstance(prId);
                //这是camunda-operate服务器集群返回的应答，就算zeebe核心停机也能正常应答状态取值。 camunda-zeebe和camunda-tasklist俩个模块集群的死活和这里没关联的!。
                if(null==instance || ProcessInstanceState.ACTIVE == instance.getState() || ProcessInstanceState.CANCELED == instance.getState())
                    return false;
                else
                    return  true;
                //其它状态=COMPLETED, CANCELED;
            } catch (OperateException e) {
                String errorstr=e.getMessage();
                //和其它类型异常区分开：
                //没有找到流程实例ID的报错是"/v1/process-instances/2253017914199304 : 404 "
                if(errorstr.length() <75) {
                    int find404 = errorstr.lastIndexOf(": 404");
                    if (find404 > 21)
                        return true;
                }
                e.printStackTrace();
            }
        }
        return false;        //查询失败的
    }
    /**流程实例id对应 状态是活动当中;
     * 有可能遭遇临时报错 /v1/process-instances/2251799818851035 : 401 来个重试机制。
     * */
    public  boolean isProcessActiveOk(Long prId) {
        if(null!=client) {
            int numCalls = 0;
            do {
                numCalls++;
                if(numCalls>1) {
                    try {
                        Thread.sleep(1000);     //错误的休息 1秒
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
                try {
    //                ProcessInstanceFilter instanceFilter = new ProcessInstanceFilter.Builder().bpmnProcessId("customer_onboarding_en").startDate(new DateFilter(new Date(), DateFilterRange.MONTH)).build();
    //                SearchQuery instanceQuery = new SearchQuery.Builder().filter(instanceFilter).size(20).sort(new Sort("state", SortOrder.ASC)).build();
    //                List<ProcessInstance> list = client.searchProcessInstances(instanceQuery);
    //                SearchResult<ProcessInstance> result = client.searchProcessInstanceResults(instanceQuery);
                    ProcessInstance instance = client.getProcessInstance(prId);
                    //这是camunda-operate服务器集群返回的应答，就算zeebe核心停机也能正常应答状态取值。 camunda-zeebe和camunda-tasklist俩个模块集群的死活和这里没关联的!。
                    if(null==instance || ProcessInstanceState.COMPLETED == instance.getState() || ProcessInstanceState.CANCELED == instance.getState())
                        return false;
                    else if(ProcessInstanceState.CANCELED == instance.getState()){
                        assertShouldNeverHappen("旧流程有错:prId={}"+prId);
                    }
                    else
                        return  true;
                } catch (OperateException e) {
                    String errorstr=e.getMessage();
                    //和其它类型异常区分开：
                    //没有找到流程实例ID的报错是"/v1/process-instances/2253017914199304 : 404 "
                    if(errorstr.length() <75) {
                        int find404 = errorstr.lastIndexOf(": 404");
                        if (find404 > 21)
                            return false;
                        int find401 = errorstr.lastIndexOf(": 401");
                        if (find401 > 21)
                            continue;        //临时错401的？
                    }
                    e.printStackTrace();
                }
            } while (numCalls < 3);
            assertShouldNeverHappen("查流程引擎错:prId="+prId);       //做3次了都不能成功啊
        }
        assertShouldNeverHappen("流程引擎宕机");
        return false;        //查询失败的
    }
}


/*旧版本的有org.camunda.bpm.engine.delegate.DelegateTask; 如今新的是@JobWorker对应原来的Delegate类的{旧版的监听事件}JavaDelegate。
问题：Job worker 工作者代码必须是幂等的。
直接用zeebe-client.Api太不友好, 没法直接的对UserTask读取当前流程实例  https://github.com/camunda-community-hub/spring-zeebe
*/
